5.16. Первая программа
Первая программа
Ассемблер — это язык программирования низкого уровня, максимально приближенный к машинному коду, который исполняет процессор. Каждая команда на ассемблере соответствует одной или нескольким инструкциям процессора. Это позволяет программисту управлять аппаратными ресурсами напрямую: регистрами центрального процессора, памятью, прерываниями и портами ввода-вывода.
Программы на ассемблере обладают высокой производительностью и минимальным размером. Они находят применение в системном программировании, разработке драйверов устройств, встраиваемых системах, операционных системах, а также в задачах, где критичны скорость и контроль над оборудованием. Изучение ассемблера даёт глубокое понимание того, как работает компьютер на самом низком уровне — от загрузки программы до выполнения каждой инструкции.
Архитектура и диалекты
Ассемблер не является единым языком. Он зависит от архитектуры процессора. Наиболее распространённые архитектуры для обучения — x86 и x86-64 (Intel/AMD), используемые в большинстве персональных компьютеров. Для этих архитектур существуют два основных синтаксических стиля:
- Intel Syntax — используется в большинстве учебных материалов, отладчиков Windows и популярных ассемблеров, таких как MASM и NASM.
- AT&T Syntax — применяется в Unix-подобных системах, особенно в связке с GCC и GAS (GNU Assembler).
Для начала рекомендуется использовать Intel Syntax, так как она более интуитивна для новичков: операнды указываются в порядке «назначение, источник», а регистры и константы записываются без лишних префиксов.
Можно ли писать на ассемблере в Windows
Да, написание и запуск программ на ассемблере возможны в операционной системе Windows. Для этого требуется установить комплект инструментов, включающий ассемблер, компоновщик (линкер) и, при необходимости, отладчик. Современные версии Windows поддерживают 64-битное программирование, но многие учебные примеры построены на 32-битной модели, так как она проще для понимания и имеет меньше ограничений со стороны операционной системы.
Необходимые инструменты
Для написания первой программы на ассемблере в Windows понадобятся следующие компоненты:
1. Ассемблер
Наиболее подходящий выбор для начинающих — NASM (Netwide Assembler). Это свободный, кроссплатформенный ассемблер, поддерживающий Intel Syntax и генерирующий объектные файлы в форматах COFF (для Windows) и ELF (для Linux). NASM активно поддерживается, имеет простой синтаксис и обширную документацию.
2. Компоновщик (Linker)
После ассемблирования исходного кода получается объектный файл, который необходимо объединить с системными библиотеками и оформить в исполняемый файл. В Windows для этой цели используется link.exe из Microsoft Visual Studio Build Tools или ld из MinGW-w64.
Для упрощения процесса рекомендуется установить MinGW-w64 — набор инструментов GNU для Windows, включающий компилятор GCC, ассемблер GAS, компоновщик ld и другие утилиты. Однако для работы с NASM удобнее использовать link.exe из официальных инструментов Microsoft.
3. Текстовый редактор или IDE
Ассемблер не требует сложной среды разработки. Достаточно любого текстового редактора, поддерживающего подсветку синтаксиса, например:
- Visual Studio Code с расширением для NASM,
- Notepad++,
- Sublime Text.
Полноценные IDE, такие как SASM (SimpleASM), специально созданы для обучения ассемблеру. SASM включает встроенный NASM, компоновщик, отладчик и окно вывода, что делает его идеальным выбором для первых шагов.
4. Отладчик (опционально, но полезно)
Для анализа выполнения программы можно использовать x64dbg — современный отладчик для Windows, поддерживающий как 32-, так и 64-битные приложения. Он позволяет просматривать регистры, память, стек и пошагово выполнять инструкции.
Пошаговая установка инструментов
Шаг 1: Установка SASM (рекомендуемый способ для новичков)
- Перейдите на официальный сайт SASM: https://dman95.github.io/SASM/
- Скачайте установщик для Windows.
- Запустите установщик и следуйте инструкциям.
- После установки откройте SASM. По умолчанию он настроен на использование NASM и генерацию 32-битных программ под Windows.
SASM автоматически устанавливает всё необходимое: ассемблер, компоновщик, среду выполнения. Это избавляет от необходимости настраивать пути и команды вручную.
Шаг 2: Альтернативная установка (вручную)
Если вы предпочитаете полный контроль:
-
Скачайте NASM с официального сайта: https://www.nasm.us/
- Выберите Windows-версию (обычно это ZIP-архив).
- Распакуйте содержимое в папку, например
C:\nasm. - Добавьте эту папку в переменную среды
PATH.
-
Установите Microsoft Visual Studio Build Tools:
- Перейдите на страницу загрузки Visual Studio.
- Выберите «Build Tools for Visual Studio».
- В установщике отметьте компонент «C++ build tools», включающий
link.exe.
-
Убедитесь, что в командной строке доступны команды
nasmиlink.
Написание первой программы: Hello, World!
Программа на ассемблере для Windows может использовать системные вызовы через API Win32. Простейший способ вывести текст — вызвать функцию MessageBoxA из библиотеки user32.dll.
Ниже приведён пример программы на 32-битном ассемблере с использованием NASM-синтаксиса:
section .data
msg db 'Hello, World!', 0
caption db 'My First ASM Program', 0
section .text
global _start
extern _MessageBoxA@16
extern _ExitProcess@4
_start:
; Вызов MessageBoxA(hWnd, lpText, lpCaption, uType)
push 0 ; uType = MB_OK
push caption ; lpCaption
push msg ; lpText
push 0 ; hWnd = NULL
call _MessageBoxA@16
; Завершение программы
push 0 ; exit code
call _ExitProcess@4
Этот код делает следующее:
- Определяет две строки в секции данных: сообщение и заголовок окна.
- В секции кода вызывает функцию
MessageBoxAиз Windows API. - После закрытия окна вызывает
ExitProcessдля корректного завершения.
Сборка и запуск вручную
Если вы используете NASM и link.exe вручную, выполните следующие команды в командной строке:
-
Ассемблирование:
nasm -f win32 hello.asm -o hello.obj -
Компоновка:
link /subsystem:windows /entry:_start hello.obj user32.lib kernel32.lib
В результате будет создан исполняемый файл hello.exe, который можно запустить двойным щелчком.
Сборка и запуск в SASM
- Откройте SASM.
- Создайте новый файл.
- Вставьте приведённый выше код.
- Нажмите кнопку Run (или F9).
- Появится окно с надписью «Hello, World!».
SASM автоматически обрабатывает все этапы сборки и подключает необходимые библиотеки.
Особенности 64-битного программирования
В 64-битной Windows системные вызовы через WinAPI работают иначе: соглашение о вызовах меняется, параметры передаются через регистры, а не через стек. Для первого знакомства рекомендуется начинать с 32-битного режима, так как он проще и лучше документирован в учебных материалах.
Структура программы на ассемблере
Программа на ассемблере состоит из логических блоков, называемых секциями. Каждая секция содержит определённый тип данных или инструкций. Наиболее важные секции в программе для Windows:
.data— содержит инициализированные данные: строки, числа, константы..bss— содержит неинициализированные данные (например, буферы, массивы, которые будут заполнены позже)..text— содержит исполняемый код, то есть последовательность машинных инструкций.
Эти секции отражают внутреннюю организацию исполняемого файла и соответствуют сегментам памяти, выделяемым операционной системой при запуске программы.
Регистры процессора
Центральный процессор использует небольшие сверхбыстрые ячейки памяти — регистры — для временного хранения данных и адресов. В архитектуре x86 основные 32-битные регистры включают:
EAX,EBX,ECX,EDX— общего назначения,ESI,EDI— индексные регистры для работы со строками и массивами,ESP— указатель стека,EBP— базовый указатель для доступа к параметрам функций.
В 64-битном режиме регистры расширяются до 64 бит (RAX, RBX и так далее), но для первых шагов достаточно понимания 32-битной модели.
Регистры играют роль переменных самого низкого уровня. Все операции в ассемблере выполняются над регистрами или между регистром и памятью.
Стек и вызовы функций
Стек — это область памяти, используемая для передачи параметров в функции, хранения локальных переменных и возврата управления после вызова. Операции push и pop добавляют и извлекают значения из стека.
При вызове функции через call процессор автоматически помещает в стек адрес возврата. Функция завершается командой ret, которая извлекает этот адрес и передаёт управление обратно вызывающему коду.
В Windows API функции используют соглашение о вызовах stdcall, при котором параметры передаются через стек в обратном порядке (справа налево), а очистка стека выполняется самой функцией. Это отличает их от соглашения cdecl, где очистка стека лежит на вызывающей стороне.
Имена функций в 32-битном stdcall имеют специальный суффикс вида @N, где N — общее количество байт параметров. Например, _MessageBoxA@16 означает, что функция принимает 16 байт аргументов (четыре 32-битных параметра).
Понимание имён и экспорта
Ключевое слово global _start указывает ассемблеру, что метка _start является точкой входа в программу. Компоновщик ищет именно эту метку, чтобы определить, с какого места начинать выполнение.
Ключевое слово extern объявляет, что функция определена вне текущего файла — в системной библиотеке. Например, _MessageBoxA@16 находится в user32.dll, а _ExitProcess@4 — в kernel32.dll. При компоновке эти имена связываются с реальными адресами в памяти.
Альтернативный подход: DOS и эмуляторы
Если использовать современный Windows API кажется сложным, можно начать с эмуляции старой среды MS-DOS. Для этого подходит эмулятор DOSBox вместе с ассемблером TASM или MASM, а также утилитой LINK.EXE из пакета Microsoft Macro Assembler.
Пример простой программы для DOS:
section .text
global _start
_start:
mov ah, 09h ; функция вывода строки
mov dx, msg ; адрес строки
int 21h ; вызов DOS
mov ah, 4Ch ; завершение программы
mov al, 0 ; код возврата
int 21h
section .data
msg db 'Hello, World!$'
Здесь используется прерывание DOS int 21h для вывода текста. Строка должна заканчиваться символом $. Такой код нельзя запустить напрямую в современной Windows, но он отлично работает в DOSBox или через эмулятор emu8086.
Этот подход полезен для обучения, так как исключает необходимость взаимодействия с современными библиотеками и позволяет сосредоточиться на базовых инструкциях процессора.
Отладка первой программы
После успешного запуска программы полезно заглянуть «внутрь» её выполнения. Отладчик x64dbg позволяет:
- поставить точку останова на метке
_start, - просматривать содержимое регистров до и после каждой инструкции,
- наблюдать, как изменяется стек при вызове
call, - видеть, какие системные библиотеки загружены в память.
Даже простой просмотр того, как адрес строки передаётся в стек, даёт ощущение контроля над машиной.